/**
*   @file main.c
* 	Programa main.
*   Fecha de ultima modificacion 06/11/2007.
*/
#include "main.h"

int main(int argc, char **argv)
{
	imageADT image;
	queueADT operationQ;
	operationT queueElement;
	operationT * operation;
	char * optBkp, * source = NULL, * dest = NULL;
	int nextOpt, sourceExists = 0, destExists = 0, compExists = 0, fail = 0;

		
	/* Si se ejecuta sin parametros ni opciones sale*/
	if( argc == 1 )
	{
		usage(argv[0]);
		return 0;
 	}

	if( (operationQ = newQueue(copyQElement, freeQElement)) == NULL )
		return 1;


/*
	Se cicla hasta que se terminen de levantar todos los parametros
	con sus respectivos argumentos. Segun el parametro y encola la funcion
 	a llamar.
 
 	La composicion no se encola porque es independiente del resto de las
 	operaciones al igual que el "help".
 
 	Tanto "-s" y "-d" utilizan flags para saber si se abrieron dos archivos.
 
 	"?" y "-1" es para que si se pasan parametros no existentes, en ese
 	caso se imprime la ayuda y se sale.
 	
*/

	while( (nextOpt = getopt_long(argc, argv,"hr:c:m:i:s:d:", longOpt, NULL)) != -1 )
	{

		switch( nextOpt )
		{
			case 'h':
					usage(argv[0]);
					return 0;
					
					break;

			case 'r':
					optBkp = optarg;
					if( (queueElement.opt = copyString(optBkp)) == NULL )
						return 1;

					queueElement.ptr = rotation_wrapper;
					
					if( !enqueue(operationQ, &queueElement) )
						return 1;

					free(queueElement.opt);	
					break;

			case 'c':
					optBkp = optarg;
					imageADT imageAuxi;
					
					if( (imageAuxi = openImage(optBkp)) == NULL )
						return 1;
					
					if( !composition(imageAuxi) )
						return 1;
				
					if( !closeImage(imageAuxi) )
						return 1;

					compExists = 1;

					break;

			case 'm':
					optBkp = optarg;
					if( (queueElement.opt = copyString(optBkp)) == NULL )
						return 1;

					queueElement.ptr = mirror_wrapper;
					
					if( !enqueue(operationQ, &queueElement) )
						return 1;

					free(queueElement.opt);						
					break;
					
			case 'i':
					optBkp = optarg;
					if( (queueElement.opt = copyString(optBkp)) == NULL )
						return 1;

					queueElement.ptr = intersection_wrapper;
					
					if( !enqueue(operationQ, &queueElement) )
						return 1;

					free(queueElement.opt);						
					break;

			case 's':
					optBkp = optarg;
					if( sourceExists )
						return 1;

					if( (source = copyString(optBkp)) == NULL )					
						return 1;
					
					sourceExists = 1;
					
					break;

			case 'd':
					optBkp = optarg;
					if( destExists )
						return 1;
					
					if( (dest = copyString(optBkp)) == NULL )
						return 1;
					
					destExists = 1;
					
					break;

			case '?':
			case -1:
					usage(argv[0]);
					break;

			default:
					abort();
		}
	}

/*
	Si no hay imagen origen ni destino y no se uso la composicion
 	muestra la ayuda y sale.
*/

	if( source == NULL || dest == NULL )
	{
		if(!compExists)
			usage(argv[0]);
		else
			freeQueue(operationQ);
				
		return 0;
	}

/*
	Se abre la imagen a usar.
*/
	if( (image = openImage(source)) == NULL )
	{
		printf("openImage failed\n");
		return 1;
	}
	if( !headerCheck(image) )
	{
		printf("Wrong bmp format\n");
		return 1;
	}
	
/*
	Se cicla desencolando las operaciones, mientras que
 	no hayan errores sigue ejecutando. Si falla algo se
 	prende el flag de fail y sale.
 
 	Si no falla se salva la imagen, sino se imprime un
 	cartel de Error.
*/

	while( !queueIsEmpty(operationQ) && !fail )
	{
		operation = dequeue(operationQ);
		if( !operation->ptr(image, operation->opt) )
			fail = 1;
		
		freeQElement(operation);
	}

	if( !fail )
	{
		if( !saveImage(image, dest) )
		{
			printf("saveImage failed\n");
			return 1;
		}
	}
	else
		printf("Error\n");


/*
	Haya errores o no, se cierra la imagen
 	y se libera la memoria usada.
*/

	if( !closeImage(image) )
	{
		printf("closeImage failed\n");
		return 1;
	}

	freeQueue(operationQ);
	free(source);
	free(dest);
	
	return 0;
}


int intersection_wrapper(imageADT image, char * opt)
{
	imageADT image2;
	
	if( image == NULL || opt == NULL )
		return 0;

	char * file, * rgb;
	int blue=255, green=255, red = 255;

/*
	Se alloca el suficiente lugar en los dos strings.
*/

	if( (file = malloc(strlen(opt) + 1)) == NULL )
		return 0;
		
	if( (rgb = malloc(strlen(opt) + 1)) == NULL )
		return 0;

/*
	Se intentan leer los dos parametros. Si sscanf devuelve
	mas de uno es porque existe el parametro rgb(). Entonces
 	lo levanto.
*/

	if( sscanf(opt, "%s %s", file, rgb) > 1 )
		 sscanf(rgb, "rgb(%d,%d,%d)", &red, &green, &blue);

	if( blue < 0 || blue > 255 || green < 0 || green > 255 || red < 0 || red > 255 )
		return 0;

/*
	Si todo esta bien abro la segunda imagen y aplico la interseccion.
 	Si todo sale bien, el resultado queda en la primera imagen, entonces
 	se libera lo que ya no se va a usar y salgo.
*/

	if( (image2 = openImage(file)) == NULL )
		return 0;

	if( !intersection(image, image2, (unsigned char)red, (unsigned char)green, (unsigned char) blue) )
		return 0;
	
	if( !closeImage(image2) )
		return 0;

	free(file);
	free(rgb);

	return 1;
}

int mirror_wrapper(imageADT image, char * opt)
{

	if( image == NULL || opt == NULL )
		return 0;
	
/*
	Si el parametro no es X o Y hay algo mal y sale.
*/

	switch( *opt )
	{
		case 'x':
				if( !mirror(image, 1) )
					return 0;
					
				break;
				
		case 'y':
				if( !mirror(image, 0) )
					return 0;
					
				break;
	
		default:
				return 0;
	}
	
	if( strlen(opt) > 1 )
		return 0;
	
	return 1;
}

int rotation_wrapper(imageADT image, char * opt)
{
	int n, orientation;
	char letter;

/*
	Se guarda en n el valor numerico de la cantidad de
 	rotaciones a hacer. Se hace modulo 4 porque sino
 	se harian rotaciones de mas.
 	Si n < 0 es un error, y si n == 0, se termina.
*/		

	if( (sscanf(opt, "%d%c", &n, &letter)) != 2 )
		return 0;

	if( (n = n%4) < 0 )
		return 0;

	if( n == 0 )
		return 1;

/*
	Se setea orientation segun el ultimo valor de opt.
*/	
	switch( letter )
	{
		case 'h':
				orientation = 0;
				break;
				
		case 'H':
				orientation = 1;
				break;
	
		default:
				return 0;
	}


/*
	Se rota n veces.
*/	
	while ( n-- )
		if( !rotation(image, orientation) )
			return 0;
	
	return 1;
}

char * copyString(char * source)
{
	char * string;
	
	if( source == NULL )
		return NULL;
	
	if( (string = malloc(strlen(source) +1)) == NULL )
		return NULL;
	
	strcpy(string, source);

	return string;
}


void * copyQElement(void * queueE)
{
	operationT * a;
	operationT * b = queueE;

	if( b == NULL)
		return NULL;

	if( (a = malloc(sizeof(operationT))) == NULL )
		return NULL;
	
	if( (a->opt = malloc(strlen(b->opt) + 1)) == NULL )
		return NULL;

	strcpy(a->opt, b->opt);
	a->ptr = b->ptr;	
	
	return a;
}

void freeQElement(void * queueE)
{
	operationT * a = queueE;

	if( a == NULL )
		return;
	
	free(a->opt);
	free(a);

	return;
}

void usage(char * prgName)
{

	printf("Uso: %s opciones [argumentos...]\n\n", prgName);
	printf("Recuerde que los parametros -s y -d son obligatorios  \n\n");

	printf(" -h, --help                                           \n");
	printf("                  Imprime esta ayuda                  \n\n");
  	printf(" -r n(h|H), --rotate n(h|H)                           \n");
	printf("                  Rota n veces 90 grados en dir       \n");
	printf("                  horaria(h) u antihoraria (H)        \n\n");
	printf(" -m (x|y), --mirror (x|y)                             \n");
	printf("                  Espeja la imagen horizontalmente (x)\n");
	printf("                  o verticalmente (y)                 \n\n");
	printf(" -i \"file\", --intersection \"file rgb(xxx,yyy,zzz)\"\n");
	printf("                  Realiza la interseccion de la imagen\n");
	printf("                  con file. Si no se agrega rgb(x,y,z)\n");
	printf("                  se usara el color blanco. Se debe   \n");		
	printf("                  usar comillas para que funcione.    \n\n");
	printf(" -s, --source file                                    \n");
	printf("                  Imagen bmp a leer                   \n\n");
	printf(" -d, --destination file                               \n");
	printf("                  Imagen bmp a escribir               \n\n");
	printf(" -c file, --composition file                          \n");
	printf("                  Muestra la composicion de la imagen \n\n");	
	
	return;
}


